<template>
  <v-card>
    <v-card-title>{{ t('shortcuts.title') }}</v-card-title>
    <v-card-text>
      <v-list>
        <template v-for="group in groups" :key="group.id">
          <v-list-subheader>{{ group.title }}</v-list-subheader>
          <v-list-item v-for="item in group.items" :key="item.id" link :ripple="false">
            <v-list-item-title class="ml-4">{{ item.name }}</v-list-item-title>
            <template #append>
              <v-text-field
                :ref="el => setFieldRef(item.id, el)"
                :class="{ 'conflict-pulse': conflictMark[item.id] }"
                hide-details
                :model-value="getDisplayValue(item)"
                :placeholder="t('shortcuts.placeholder')"
                readonly
                width="250px"
                @blur="onFieldBlur(item.id)"
                @focus="onFieldFocus(item.id)"
              />
            </template>
          </v-list-item>
        </template>
      </v-list>
    </v-card-text>
  </v-card>
</template>

<script lang="ts" setup>
/**
 * 设置页 - 快捷键
 * 展示按分类的有效快捷键（默认值 + overrides），编辑时仅写入差异（与默认一致则删除 override）
 */

import { computed, onBeforeUnmount, reactive, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { shortcutsState } from '@/stores/globalState'
import type { CommandId } from '~types/global-state'
import { getDefaultShortcuts, getEffectiveShortcutGroups } from '~shared/utils/shortcuts'
import { formatShortcutForDisplay } from '@/utils/shortcutFormat'
const { t, locale } = useI18n()

const groups = computed(() =>
  getEffectiveShortcutGroups(shortcutsState.state.overrides, locale.value as any)
)

const isMac = _pre_.platform === 'darwin'

function toCommonShortcut(key: string): string {
  if (!key) return ''
  // 将系统修饰键映射为通用修饰键；大小写按 Electron Accelerator 习惯
  let s = key
  // 宽松处理：包含 Command/Meta/Cmd -> CmdOrCtrl；Ctrl -> CmdOrCtrl；Option/Opt -> Alt
  s = s.replace(/\b(Command|Meta|Cmd)\b/gi, 'CmdOrCtrl')
  s = s.replace(/\b(Ctrl|Control)\b/gi, 'CmdOrCtrl')
  s = s.replace(/\b(Option|Opt)\b/gi, 'Alt')
  s = s.replace(/⌥/g, 'Alt')
  // 去重：例如意外出现 "CmdOrCtrl+CmdOrCtrl+F" -> 合并一个
  s = s.replace(/CmdOrCtrl\+CmdOrCtrl\+/g, 'CmdOrCtrl+')
  return s
}

/** 录制状态 */
const recordingId = ref<CommandId | null>(null)
const pressed = reactive({ ctrl: false, shift: false, alt: false, meta: false })
const recordingDisplay = ref('')
let removeListeners: (() => void) | null = null

/** 仅保存差异：若与默认值相同则删除 override；否则写入 key 到 overrides */
function _setKeyOverride(id: CommandId, rawKey: string) {
  // 存储前转换为通用修饰键（CmdOrCtrl）
  const key = toCommonShortcut(rawKey)
  const defaults = getDefaultShortcuts(locale.value as any)
  const def = defaults.find(s => s.id === id)
  if (!def) return
  const sameAsDefault = def.key === key
  const current = shortcutsState.state.overrides
  const next: Record<CommandId, string> = { ...current }
  if (sameAsDefault) {
    // 与默认一致则移除覆盖，保持“仅存差异”的持久化策略
    delete next[id]
  } else {
    next[id] = key
  }
  shortcutsState.set('overrides', next)
}

/** 输入框展示值：录制中显示实时修饰键，否则显示当前快捷键 */
function getDisplayValue(item: { id: CommandId; key: string }) {
  if (recordingId.value === item.id) return recordingDisplay.value
  return formatShortcutForDisplay(item.key)
}

/** 将按下的修饰键格式化为字符串（用于实时显示） */
function buildModifiersLabel(): string {
  const mods: string[] = []
  if (isMac) {
    if (pressed.meta) mods.push('Cmd')
  } else {
    if (pressed.ctrl) mods.push('Ctrl')
    if (pressed.meta) mods.push('Meta')
  }
  if (pressed.shift) mods.push('Shift')
  if (pressed.alt) mods.push('Alt')
  return mods.join('+')
}

/** 将 KeyboardEvent.key 规范为 Electron Accelerator 可识别的键名 */
function normalizeKeyName(key: string): string {
  if (!key) return ''
  // 处理字母
  if (/^[a-z]$/.test(key)) return key.toUpperCase()
  // 数字与功能键
  if (/^F\d{1,2}$/.test(key)) return key.toUpperCase()
  if (/^\d$/.test(key)) return key
  // 方向键
  if (key === 'ArrowUp') return 'Up'
  if (key === 'ArrowDown') return 'Down'
  if (key === 'ArrowLeft') return 'Left'
  if (key === 'ArrowRight') return 'Right'
  // 常见特殊键按原样或首字母大写
  const map: Record<string, string> = {
    ' ': 'Space',
    Escape: 'Esc',
    Esc: 'Esc',
    Enter: 'Enter',
    Return: 'Enter',
    Backspace: 'Backspace',
    Delete: 'Delete',
    Insert: 'Insert',
    Home: 'Home',
    End: 'End',
    PageUp: 'PageUp',
    PageDown: 'PageDown',
    Tab: 'Tab',
    Minus: 'Minus',
    Equal: 'Equal'
  }
  return map[key] ?? key
}

/** 冲突检测：在现有 groups 中查找相同组合键（排除自身） */
function findConflict(commonKey: string, targetId: CommandId) {
  for (const g of groups.value) {
    for (const it of g.items) {
      if (it.id !== targetId && it.enabled !== false && it.key === commonKey) {
        return { item: it, group: g }
      }
    }
  }
  return null
}

const conflictMark = reactive<Record<string, boolean>>({})
const fieldRefs = new Map<CommandId, any>()

function setFieldRef(id: CommandId, el: any) {
  if (el) fieldRefs.set(id, el)
}

function focusField(id: CommandId) {
  const comp = fieldRefs.get(id)
  const el = comp?.$el ?? comp
  el?.scrollIntoView?.({ behavior: 'smooth', block: 'center' })
}

function showConflict(id: CommandId) {
  focusField(id)
  conflictMark[id] = true
  window.setTimeout(() => {
    conflictMark[id] = false
  }, 900)
}

function endRecording() {
  recordingId.value = null
  pressed.ctrl = pressed.shift = pressed.alt = pressed.meta = false
  recordingDisplay.value = ''
  if (removeListeners) {
    removeListeners()
    removeListeners = null
  }
}

function blurActive() {
  const el = document.activeElement as unknown as { blur?: () => void } | null
  if (el && typeof el.blur === 'function') el.blur()
}

function onFieldFocus(id: CommandId) {
  startRecording(id)
}

function onFieldBlur(id: CommandId) {
  if (recordingId.value === id) endRecording()
}

function startRecording(id: CommandId) {
  // 若已在录制，则先结束
  endRecording()
  recordingId.value = id
  recordingDisplay.value = ''

  const onKeyDown = (e: KeyboardEvent) => {
    if (recordingId.value !== id) return
    e.preventDefault()
    e.stopPropagation()

    // ESC: 取消录制并不保存，也不计入组合键
    if (e.key === 'Escape' || e.key === 'Esc') {
      endRecording()
      blurActive()
      return
    }

    // 更新修饰键状态
    pressed.ctrl = e.ctrlKey
    pressed.shift = e.shiftKey
    pressed.alt = e.altKey
    pressed.meta = e.metaKey

    const isModifier = ['Control', 'Shift', 'Alt', 'Meta'].includes(e.key)
    if (isModifier) {
      // 仅修饰键：实时显示
      recordingDisplay.value = buildModifiersLabel()
      return
    }

    // 普通键：立即结束录制并尝试保存
    const mods = buildModifiersLabel()
    const keyName = normalizeKeyName(e.key)
    const rawCombo = mods ? `${mods}+${keyName}` : keyName
    const common = toCommonShortcut(rawCombo)

    const conflict = findConflict(common, id)
    if (conflict) {
      endRecording()
      blurActive()
      showConflict(conflict.item.id)
      return
    }
    console.log('[录制]', rawCombo)

    _setKeyOverride(id, rawCombo)
    endRecording()
    blurActive()
  }

  const onKeyUp = (e: KeyboardEvent) => {
    if (recordingId.value !== id) return
    e.preventDefault()
    e.stopPropagation()
    // 松开修饰键时更新显示
    pressed.ctrl = e.ctrlKey
    pressed.shift = e.shiftKey
    pressed.alt = e.altKey
    pressed.meta = e.metaKey
    recordingDisplay.value = buildModifiersLabel()
  }

  window.addEventListener('keydown', onKeyDown, { capture: true })
  window.addEventListener('keyup', onKeyUp, { capture: true })
  removeListeners = () => {
    window.removeEventListener('keydown', onKeyDown, { capture: true } as any)
    window.removeEventListener('keyup', onKeyUp, { capture: true } as any)
  }
}

onBeforeUnmount(() => {
  endRecording()
})
</script>

<i18n>
{
  "zhHans": {
    "shortcuts": {
      "title": "快捷键设置",
      "placeholder": "设置快捷键"
    }



  },
  "en": {
    "shortcuts": {
      "title": "Shortcuts Settings",
      "placeholder": "Set shortcut"
    }
  }
}
</i18n>

<style scoped>
.conflict-pulse {
  animation: conflictPulse 0.9s ease;
}
@keyframes conflictPulse {
  0% {
    box-shadow: 0 0 0 0 rgba(255, 71, 87, 0);
  }
  30% {
    box-shadow: 0 0 0 4px rgba(255, 71, 87, 0.35);
  }
  100% {
    box-shadow: 0 0 0 0 rgba(255, 71, 87, 0);
  }
}
</style>
